/**
 * ###################### 通知广播中心 #######################
 * 
 * 1. 添加监听
 * 
 * addNormalNotificationObserver
 * addOnceNotificationObserver
 * addAlwaysNewNotificationObserver
 * 调用者可以在需要监听通知的地方调用以上任一复合要求的方法注册通知监听
 * 
 * 2. 移除监听
 * 
 * removeNotificationObserver
 * 在不需要监听的时候调用调用以上方法移除对通知的监听，页面、组件 被卸载时要及时移除监听
 * 
 * 3. 发送通知
 * 
 * postNotification
 * 在需要的地方调用以上方法发送通知,通知监听者调用响应方法进行响应处理
 * 
 */
const __notificationList = {}; // 通知对象存储器
const NOTIFICATION_TYPE = {
	NORMAL: 0, // 正常 可添加多个监听者
	ONCE: 1, // 只允许保留最初始的监听者
	ALWAYS_NEW: 2, // 只通知最新监听者
} // 通知对象类型


/**
 * ############## 通知对象 ###############
 */
class Notification {
	name = ""; // notification 名称/标识
	notificationType = NOTIFICATION_TYPE.NORMAL; // 通知模式类型
	notificationObserverList = []; // 监听该通知的 监听者对象列表
	/**
	 * 通知对象 构造器
	 * @param {string} name 名称
	 * @param {NOTIFICATION_TYPE} notificationType 类型
	 */
	constructor(name, notificationType) {
		if (notificationType) {
			this.notificationType = notificationType;
		}
		this.name = name;
	}
	/**
	 * 移除监听对象
	 * @param {object} observer 监听对象
	 */
	removeObserver(observer) {
		if (!(observer instanceof NotificationObserver)) {
			throw new Error('removeObserver 请传入 NotificationObserver 对象');
		}
		if (this.notificationObserverList.length == 0) {
			return;
		}
		let removeIndex = -1;
		this.notificationObserverList.forEach((item, index) => {
			if (cmp(item.observer, observer.observer)) {
				removeIndex = index;
			}
		});
		if (removeIndex != -1) {
			this.notificationObserverList.splice(removeIndex, 1);
		}
	}

	/**
	 * 移除所有监听对象
	 */
	removeAllObserver() {
		if (this.notificationObserverList.length == 0) {
			return;
		}
		this.notificationObserverList = [];
	}

	/**
	 * 添加监听对象
	 * @param {object} observer 监听对象
	 */
	addObserver(observer) {
		if (!(observer instanceof NotificationObserver)) {
			throw new Error('addObserver 请传入 NotificationObserver 对象');
		}
		switch (this.notificationType) {
			case NOTIFICATION_TYPE.NORMAL: // 正常监听模式
			{
				// 监听列表为空，直接插入监听列表
				if (this.notificationObserverList.length == 0) {
					this.notificationObserverList.push(observer);
					return;
				}
				// 监听列表不为空，先查找监听列表是否有该对象
				let insertIndex = -1;
				this.notificationObserverList.forEach((item, index) => {
					if (cmp(item.observer, observer.observer)) {
						insertIndex = index;
					}
				});
				if (insertIndex == -1) { // 如果没有该对象，列表插入
					this.notificationObserverList.push(observer);
				} else { // 如果有，进行替换
					this.notificationObserverList[insertIndex] = observer;
				}
			}
			break;
		case NOTIFICATION_TYPE.ONCE: // 单次监听模式，只保存第一监听者
			// 监听列表是否为空， 为空插入监听者， 不为空不进行操作，插入失败
			if (this.notificationObserverList.length == 0) {
				this.notificationObserverList = [observer];
			}
			break;
		case NOTIFICATION_TYPE.ALWAYS_NEW: // 仅通知最新监听者模式，只有监听者列表最后一位才会响应广播通知，不要在异步中操作添加和移除通知，存在通知错误监听者的风险
		{
			// 监听列表为空，直接插入
			if (this.notificationObserverList.length == 0) {
				this.notificationObserverList.push(observer);
				return;
			}
			// 监听列表不为空，查找监听者是否存在于监听列表中
			let insertIndex = -1;
			this.notificationObserverList.forEach((item, index) => {
				if (cmp(item.observer, observer.observer)) {
					insertIndex = index;
				}
			});
			if (insertIndex == -1) { // 不存在，推入列表最后
				this.notificationObserverList.push(observer);
			} else { // 存在，先从列表中取出，再插入列表最后一位
				let tempArray = [...this.notificationObserverList];
				let tempObserver = tempArray.splice(insertIndex, 1)[0];
				tempArray.push(tempObserver);
				this.notificationObserverList = tempArray;
			}
		}
		break;
		}
	}
}

/**
 * ############## 监听者 对象 ###############
 */
class NotificationObserver {
	selector = null; // 响应方法
	observer = null; // 监听对象
	/**
	 * NotificationObserver 构造器
	 * @param {function} selector 响应方法 
	 * @param {object} observer 监听对象
	 */
	constructor(selector, observer) {
		this.selector = selector;
		this.observer = observer;
	}
}

/**
 * ############## 对外调用接口 ###############
 */

/**
 * 添加 Notice 监听
 * @param {string} name 广播名称
 * @param {function} selector 响应方法
 * @param {object} observer 监听者
 * @param {NOTIFICATION_TYPE} notificationType 是否排他通知
 */
function addNotificationObserver(name, selector, observer, notificationType) {
	if (name == null || typeof name != 'string') {
		throw new Error('addNotificationObserver 请传入 string 类型的 notice 广播名称 name');
	}
	if (selector == null || typeof selector != 'function') {
		throw new Error('addNotificationObserver 请传入 function 类型的 notice 响应方法 selector');
	}
	if (observer == null) {
		throw new Error('addNotificationObserver 广播监听者不能为空')
	}
	// 如果不存在该 通知， 创建通知
	if (__notificationList[name] == null) {
		__notificationList[name] = new Notification(name, notificationType);
	}
	// 创建监听者
	let firstNotificationObserver = new NotificationObserver(selector, observer);
	// 插入通知的监听者列表
	__notificationList[name].addObserver(firstNotificationObserver);
}

/**
 * 添加 保持最原始监听者 通知
 * @param {string} name 通知名称
 * @param {function} selector 响应方法
 * @param {object} observer 监听对象
 */
function addOnceNotificationObserver(name, selector, observer) {
	addNotificationObserver(name, selector, observer, NOTIFICATION_TYPE.ONCE);
}

/**
 * 添加 保持最新通知监听者 通知
 * @param {string} name 通知名称
 * @param {function} selector 响应方法
 * @param {object} observer 监听对象
 */
function addAlwaysNewNotificationObserver(name, selector, observer) {
	addNotificationObserver(name, selector, observer, NOTIFICATION_TYPE.ALWAYS_NEW);
}

/**
 * 添加 多个通知监听者 通知
 * @param {string} name 通知名称
 * @param {function} selector 响应方法
 * @param {object} observer 监听对象
 */
function addNormalNotificationObserver(name, selector, observer) {
	addNotificationObserver(name, selector, observer, NOTIFICATION_TYPE.NORMAL);
}

/**
 * 移除 Notice 广播
 * @param {string} name 广播名称
 * @param {object|null} observer 监听者  监听者为空时，移除 该 Notice 下所有监听者
 */
function removeNotificationObserver(name, observer) {
	// 是否有 name 对应的 通知对象
	if (__notificationList[name] != null) {
		// 监听者 是否为空 
		if (observer == null) { // 如果为空，清除 notice 下所有监听者
			__notificationList[name].removeAllObserver()
		} else { // 如果不为空，移除对应的 监听者
			__notificationList[name].removeObserver(new NotificationObserver(null, observer));
		}
	}
}

/**
 * 发送 Notice 广播
 * @param {string} name 广播名称
 * @param {any} info 附带信息
 */
function postNotification(name, info) {
	if (name == null || typeof name != 'string') {
		throw new Error('postNotification 请传入 string 类型的 notice name')
	}
	const notification = __notificationList[name];
	// 是否存在 name 对应的通知对象
	if (notification == null) { // 不存在，跳出
		return;
	}
	// 存在通知对象
	// 判断通知对象 通知模式类型
	if (notification.notificationType == NOTIFICATION_TYPE.NORMAL) { // 正常模式， 循环通知所有监听者
		notification.notificationObserverList.forEach(item => {
			item.selector(info);
		})
	} else if (notification.notificationType == NOTIFICATION_TYPE.ONCE) { // 保留最老模式，监听列表中只有一位，通知第一位
		notification.notificationObserverList[0].selector(info);
	} else if (notification.notificationType == NOTIFICATION_TYPE.ALWAYS_NEW) { // 通知最新模式，查找监听列表最后一位，调用响应方法响应通知
		notification.notificationObserverList[notification.notificationObserverList.length - 1].selector(info);
	}
}

// 用于对比两个对象是否相等
function cmp(x, y) {
	// If both x and y are null or undefined and exactly the same  
	if (x === y) {
		return true;
	}
	// If they are not strictly equal, they both need to be Objects  
	if (!(x instanceof Object) || !(y instanceof Object)) {
		return false;
	}
	// They must have the exact same prototype chain, the closest we can do is  
	// test the constructor.  
	if (x.constructor !== y.constructor) {
		return false;
	}
	for (var p in x) {
		// Inherited properties were tested using x.constructor === y.constructor  
		if (x.hasOwnProperty(p)) {
			// Allows comparing x[ p ] and y[ p ] when set to undefined  
			if (!y.hasOwnProperty(p)) {
				return false;
			}
			// If they have the same strict value or identity then they are equal  
			if (x[p] === y[p]) {
				continue;
			}
			// Numbers, Strings, Functions, Booleans must be strictly equal  
			if (typeof(x[p]) !== "object") {
				return false;
			}
			// Objects and Arrays must be tested recursively  
			if (!Object.equals(x[p], y[p])) {
				return false;
			}
		}
	}
	for (p in y) {
		// allows x[ p ] to be set to undefined  
		if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
			return false;
		}
	}
	return true;
}

module.exports = {
	addNormalNotificationObserver, // 添加 多个通知监听者 通知监听
	addOnceNotificationObserver, // 添加 保持最原始监听者 通知监听
	addAlwaysNewNotificationObserver, // 添加 保持最新通知监听者 通知监听
	removeNotificationObserver, // 移除通知监听
	postNotification, // 发送通知广播
}
